/*
 * Apple HTTP Live Streaming demuxer
 * Copyright (c) 2010 Martin Storsjo
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * Apple HTTP Live Streaming demuxer
 * http://tools.ietf.org/html/draft-pantos-http-live-streaming
 */

#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/dict.h"
#include "libavutil/time.h"
#include "avformat.h"
#include "internal.h"
#include "avio_internal.h"
#include "url.h"
#include "libavutil/avassert.h"
#include <sys/time.h>
#include <unistd.h>

#include "tmfile.h"

typedef struct tmfileContext {
	int frames;
	int stream_index[2];
    int n_variants;
    struct variant **variants;
    int cur_seq_no;
    int end_of_segment;
    int first_packet;
    int64_t first_timestamp;
    int64_t seek_timestamp;
    int seek_flags;
    AVIOInterruptCB *interrupt_callback;
} tmfileContext;

static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
    AVStream *st = avformat_new_stream(s, NULL);
    if (!st)
        return NULL;
    st->codec->codec_type = codec_type;
    avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
    return st;
}
static int tmf_vcodec_change(int tmfcodecId)
{
	int ret = -1;
	if( TMF_CODEC_ID_H264 == tmfcodecId )
	{
		ret = AV_CODEC_ID_H264;
	}else if( TMF_CODEC_ID_H263 == tmfcodecId )
	{
		ret = AV_CODEC_ID_H263;
	}else if( TMF_CODEC_ID_TIVC == tmfcodecId )
	{
		ret = AV_CODEC_ID_TIVC;
	}else if( tmfcodecId >= 0 && tmfcodecId < TMF_CODEC_ID_H264 )
	{
		ret = AV_CODEC_ID_RAWVIDEO;
	}
	return ret;
}
static int tmf_acodec_change(int tmfcodecId)
{
	int ret = -1;
	switch(tmfcodecId)
	{
	case TMF_CODEC_ID_PCM_S16LE:
		ret = AV_CODEC_ID_PCM_S16LE;
		break;
	case TMF_CODEC_ID_AAC:
		ret = AV_CODEC_ID_AAC;
		break;
	}
	return ret;
}
static void set_video_codec(AVFormatContext *s,AVStream *vstream,tmf_vcodec_info* vc) {
	AVCodecContext *vcodec = vstream->codec;
	
	vcodec->codec_id = tmf_vcodec_change(vc->codec_id);
	vcodec->bit_rate = vc->bit_rate;
	vcodec->width = vc->width;
	vcodec->height = vc->height;
	vcodec->pix_fmt = AV_PIX_FMT_YUV420P;
//	vcodec->codec_tag = 0x31637661;	
	vcodec->has_b_frames = vc->has_b_frames;
	vcodec->extradata_size = vc->extradata_size;
	if( AV_CODEC_ID_RAWVIDEO == vcodec->codec_id )
	{
		switch(vc->codec_id)
		{
			case TMF_CODEC_ID_YUV420SP:
				vcodec->pix_fmt = AV_PIX_FMT_NV12;	
				break;
			case TMF_CODEC_ID_RGB24:
				vcodec->pix_fmt = AV_PIX_FMT_RGB24;	
				break;				
			case TMF_CODEC_ID_YUYV422:
				vcodec->pix_fmt = AV_PIX_FMT_YUYV422;	
				break;				
		}
	}
	if( vc->frame_rate > 0 )
	{
		vcodec->time_base.den = vc->frame_rate;
		vcodec->time_base.num = 100;
	}
   	av_log(s, AV_LOG_WARNING, "extradata_size=%d %d %d\n",vcodec->extradata_size,vcodec->width,vcodec->codec_id);
	if( vcodec->extradata_size > 0 )
	{
    	vcodec->extradata = av_mallocz(vcodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
		avio_read(s->pb,(unsigned char *)vcodec->extradata,vcodec->extradata_size);
	}
#if 0
	vcodec->extradata_size = 36;
    	vcodec->extradata = av_mallocz(vcodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
			{
				FILE*fp=fopen("d:/extra","rb");
				if( fp )
				{
					fread(vcodec->extradata,1,vcodec->extradata_size,fp);
					fclose(fp);
				}
			}
#endif
}
static void set_audio_codec( AVFormatContext *s,AVStream *astream,tmf_acodec_info* ac) {
    AVCodecContext *acodec = astream->codec;
	acodec->codec_id = tmf_acodec_change(ac->codec_id);
	acodec->sample_rate = ac->sample_rate;
	acodec->channels = ac->channels;
	if( ac->bits_per_coded_sample > 0 )
		acodec->bits_per_coded_sample = ac->bits_per_coded_sample;
	else
		acodec->bits_per_coded_sample = av_get_bits_per_sample(acodec->codec_id);
//    av_assert0(acodec->bits_per_coded_sample > 0);
    acodec->block_align =
        acodec->bits_per_coded_sample * acodec->channels / 8;
	acodec->extradata_size = ac->extradata_size;
   	av_log(s, AV_LOG_WARNING, "extradata_size(a)=%d %d %d\n",acodec->extradata_size,acodec->sample_rate,acodec->bits_per_coded_sample);
	if( acodec->extradata_size > 0 )
	{
    	acodec->extradata = av_mallocz(acodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
		avio_read(s->pb,(unsigned char *)acodec->extradata,acodec->extradata_size);
	}
}

static int tmfile_read_header(AVFormatContext *s)
{
    int flags;
	int streamindex=0;
    tmfileContext *c = s->priv_data;

    avio_skip(s->pb, 3);
    flags = avio_r8(s->pb);
    if (!flags) {
        return AVERROR(ENOMEM);
    }
	avio_rb32(s->pb);		//version
    s->ctx_flags |= AVFMTCTX_NOHEADER;
		
    if(flags & TMF_FLAG_HASVIDEO){
		tmf_vcodec_info vcodec;
		avio_read(s->pb,(unsigned char *)&vcodec,sizeof(vcodec));
	   	av_log(s, AV_LOG_WARNING, "vcodec = %d %dX%d %d\n",vcodec.codec_id,vcodec.width,vcodec.height,vcodec.bit_rate);		
		if( -1 == tmf_vcodec_change(vcodec.codec_id) )
			return AVERROR_DECODER_NOT_FOUND;
        if(!create_stream(s, AVMEDIA_TYPE_VIDEO))
            return AVERROR_STREAM_NOT_FOUND;
   	av_log(s, AV_LOG_WARNING, "extradata_size=%d %d %d\n",vcodec.extradata_size,vcodec.width,vcodec.codec_id);
		set_video_codec(s,s->streams[streamindex],&vcodec);	
		c->stream_index[tmfVideoFrame] = streamindex;
		++streamindex;
    }
    if(flags & TMF_FLAG_HASAUDIO){
		tmf_acodec_info acodec;
		avio_read(s->pb,(unsigned char *)&acodec,sizeof(acodec));
		if( -1 == tmf_acodec_change(acodec.codec_id) )
			return AVERROR(ENOMEM);
        if(!create_stream(s, AVMEDIA_TYPE_AUDIO))
            return AVERROR(ENOMEM);
		set_audio_codec(s,s->streams[streamindex],&acodec);
		c->stream_index[tmfAudioFrame] = streamindex;
    }
    s->start_time = 0;
   	av_log(s, AV_LOG_WARNING, "*********ok\n");

	return 0;
}

static int tmfile_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    //av_log(s, AV_LOG_WARNING, "tmfile_read_packet=%p\n",pkt);
    tmfileContext *c = s->priv_data;
	tmf_frame_info frame;
    int ret= avio_read(s->pb, (unsigned char *)&frame,sizeof(tmf_frame_info));
    if( ret <= 0 )		
    {
		av_log(s, AV_LOG_ERROR,"file eof detect,%d \r\n",ret);
        return AVERROR_EOF;
    }
	if( frame.type != tmfVideoFrame && frame.type != tmfAudioFrame )
	{
		av_log(s, AV_LOG_ERROR,"err,frame type err=%d,%d %lld\r\n",ret,frame.type,avio_tell(s->pb));
        return AVERROR_EOF;
	}
	if( frame.size <= 0 )
	{
		av_log(s, AV_LOG_ERROR,"err,frame size err=%d %d %d\r\n",ret,frame.size,(int)avio_tell(s->pb));
		return AVERROR_EOF;
	}
    ret= av_get_packet(s->pb, pkt, frame.size);
    if (ret < frame.size)
    {
		av_log(s, AV_LOG_ERROR,"err,frame data err=%d %d,(frame=%d)\r\n",ret,frame.size,c->frames);
    	return AVERROR_EOF;
    }
	c->frames++;
    pkt->stream_index = c->stream_index[frame.type];
	pkt->dts = frame.dts;
	pkt->pts = frame.pts;
	
	if( frame.key_frame )
		pkt->flags |= AV_PKT_FLAG_KEY;
{
	static int dbgcounter = 0;
  //  AVCodecContext *enc  = s->streams[pkt->stream_index]->codec;
	if( dbgcounter++ < 10 )
		 av_log(NULL, AV_LOG_ERROR,"tmfile_read_packet(%d) size=%d %d dts=%lld pts=%lld(%x %x %x %x)\n",dbgcounter,frame.size,frame.key_frame,frame.dts,frame.pts,pkt->data[0],pkt->data[1],pkt->data[2],pkt->data[3]);
}
	return 0;	
}

static int tmfile_close(AVFormatContext *s)
{
//    tmfileContext *c = s->priv_data;

//    free_variant_list(c);
    return 0;
}
static int tmfile_probe(AVProbeData *p)
{
	//av_log(NULL, AV_LOG_ERROR,"tmfile_probe\r\n");
    if ( memcmp(p->buf,"TMF",3) == 0 )
        return AVPROBE_SCORE_MAX;
    return 0;
}

AVInputFormat ff_tmfile_demuxer = {
    .name           = "tmf",
    .long_name      = NULL_IF_CONFIG_SMALL("Temobi Streaming Format"),
    .priv_data_size = sizeof(tmfileContext),
    .read_probe     = tmfile_probe,
    .read_header    = tmfile_read_header,
    .read_packet    = tmfile_read_packet,
    .read_close     = tmfile_close,
};
